#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "vla.h"
#include "mylog.h"

//tuneup x to nearest 2^n - 1
#define TUNEUP32(x)		do{(x)|=(x)>>1; (x)|=(x)>>2; (x)|=(x)>>4; (x)|=(x)>>8; (x)|=(x)>>16;}while(0) 

static int grow(vla * p)
{
	assert(p != NULL);
	if(p->size < p->volume){
		return 0;
	}
	unsigned new_volume = p->size ? p->size : VLA_SIZE_DEF;
	TUNEUP32(new_volume);
	void * new_buf = realloc(p->buf, p->isize * new_volume);
	if(new_buf == NULL){
		return -1;
	}
	memset(new_buf + p->isize * p->volume, 0, p->isize * (new_volume - p->volume));
	LOGIT(LV_DETAIL, "vla realloc %u[%p] -> %u[%p]", p->volume, p->buf, new_volume, new_buf);
	p->buf = new_buf;
	p->volume = new_volume;
	return 0;
}

vla * vla_new(int isize)
{
	LOGIT(LV_DETAIL, "vla new isize %d", isize);
	if(isize <= 0){
		return NULL;
	}
	vla * p = (vla *)malloc(sizeof(struct vla));
	if(p == NULL){
		return NULL;
	}
	memset(p, 0, sizeof(struct vla));
	p->isize = isize;
	p->volume = 0;
	if(grow(p) < 0){
		free(p);
		p = NULL;
		return NULL;
	}
	return p;
}

//get the space to set
void * vla_set(vla * p, int i)
{
	if(p == NULL || i < 0){
		return NULL;
	}
	if(i >= p->size){
		p->size = i + 1;
		if(p->size > p->volume && grow(p) < 0){
			return NULL;
		}
	}
	assert(p->buf && p->size > i);
	return p->buf + p->isize * i;
}

void * vla_get(vla * p, int i)
{
	if(p == NULL || i < 0){
		return NULL;
	}
	if(i >= p->size){
		return NULL;
	}
	return p->buf + p->isize * i;
}

int vla_clear(vla * p)
{
	if(p == NULL){
		return -1;
	}
	memset(p->buf, 0, p->isize * p->size);
	p->size = 0;
	return 0;
}

int vla_free(vla * p)
{
	if(p == NULL){
		return -1;
	}
	LOGIT(LV_DETAIL, "vla free isize %d", p->isize);
	p->isize = 0;
	p->size = 0;
	p->volume = 0;
	free(p->buf);
	p->buf = NULL;
	free(p);
	return 0;
}

#ifdef VLA_MAIN
int main()
{
	LOGON();
	vla * v = vla_new(sizeof(int));
	int i = 0;
	for(i=0; i<5; i++){
		int * p = vla_set(v, i);
		*p = i;
		printf("set [%p] %d\n", p, i);
	}
	int * p = NULL;
	VLA_FOR_EACH(p, v){
		printf("get [%p] %d\n", p, *p);
	}
	vla_free(v);
	LOGOFF();
	return 0;
}
#endif
